﻿using Microsoft.Practices.Unity.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Artech.Common
{
    public static class AttributeAccessor
    {
        private static object syncHelper4type = new object();
        private static object syncHelper4Member = new object();
        private static Dictionary<Key, Attribute[]> attributes4Type = new Dictionary<Key,Attribute[]>();
        private static Dictionary<Key, Attribute[]> attributes4Member = new Dictionary<Key,Attribute[]>();

        public static IEnumerable<Attribute> GetAttributes(Type type, bool inherit)
        {
            Guard.ArgumentNotNull(type, "type");
            Key key  = new Key(type, inherit);
            Attribute[] attributes;
            if (attributes4Type.TryGetValue(key, out attributes))
            {
                return attributes;
            }
            lock (syncHelper4type)
            {
                if (attributes4Type.TryGetValue(key, out attributes))
                {
                    return attributes;
                }
                attributes = type.GetCustomAttributes(inherit).OfType<Attribute>().ToArray();
                attributes4Type.Add(key, attributes);
                return attributes;
            }
        }

        public static IEnumerable<Attribute> GetAttributes(MemberInfo member, bool inherit)
        {
            Guard.ArgumentNotNull(member, "member");
            Key key = new Key(member, inherit);
            Attribute[] attributes;
            if (attributes4Type.TryGetValue(key, out attributes))
            {
                return attributes;
            }
            lock (syncHelper4type)
            {
                if (attributes4Type.TryGetValue(key, out attributes))
                {
                    return attributes;
                }
                attributes = member.GetCustomAttributes(inherit).OfType<Attribute>().ToArray();
                attributes4Type.Add(key, attributes);
                return attributes;
            }
        }


        private class Key
        {
            public Type Type { get; private set; }
            public MemberInfo Member { get; private set; }
            public bool Inherit { get; private set; }

            public Key(Type type, bool inherit)
            {
                Guard.ArgumentNotNull(type, "type");
                this.Type = type;
                this.Inherit = inherit;
            }

            public Key(MemberInfo member, bool inherit)
            {
                Guard.ArgumentNotNull(member, "member");
                this.Member = member;
                this.Inherit = inherit;
            }

            public override int GetHashCode()
            {
                int hashCode = this.Inherit.GetHashCode();
                if (null != this.Type)
                {
                    hashCode = hashCode ^ this.Type.GetHashCode();
                }
                if (null != this.Member)
                {
                    hashCode = hashCode ^ this.Member.GetHashCode();
                }
                return hashCode;
            }

            public override bool Equals(object obj)
            {
                Key key= obj as Key;
                if (null == key)
                {
                    return false;
                }
                return key.Inherit == this.Inherit && this.Type == key.Type && this.Member == key.Member;
            }
        }

    }
}
